#pragma once

#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include<signal.h>
#include"ThreadPool.hpp"
#include "Log.hpp"
#include"Task.hpp"
#include"Daemon.hpp"

const int defaultfd = -1;
const std::string defaultip = "0.0.0.0";
const int backlog = 6; // 一般不要设置的太大
extern Log lg;

enum
{
    SocketError = 2,
    BindError,
    ListenError
};

class TcpServer;
class ThreadData
{
public:
    ThreadData(int fd, const std::string ip, uint16_t  p, TcpServer* t)
        :sockfd(fd)
        ,clientip(ip)
        ,clientport(p)
        ,tsv(t)
    {}
public:
    int sockfd;
    std::string clientip;
    uint16_t clientport;
    TcpServer* tsv;
};

class TcpServer
{
public:
    TcpServer(const uint16_t &port, const std::string &ip = defaultip)
        : listensock_(defaultfd), port_(port), ip_(ip)
    {}

    void InitServer()
    {
        // 创建套接字
        //  第一个参数：协议家族或者地址族
        //  第二个参数：socket的类型
        //  第三个参数：指定协议，先默认为0
        listensock_ = socket(AF_INET, SOCK_STREAM, 0);
        if (listensock_ < 0)
        {
            lg(Fatal, "create listensock_ error, errno: %d, errstring: %s", errno, strerror(errno));
            exit(SocketError);
        }
        lg(Info, "create listensock_ success, listensock_: %d", listensock_);

        int opt = 1;
        setsockopt(listensock_, SOL_SOCKET, SO_REUSEADDR|SO_REUSEPORT, &opt, sizeof(opt));
        //防止偶发性的服务器无法进行立即重启

        // 填充网络套接字信息--用户空间
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port_); // 主机序列转网络序列
        // 字符串ip地址直接转换位网络ip地址
        inet_aton(ip_.c_str(), &(local.sin_addr));
        // local.sin_addr.s_addr = INADDR_ANY;

        // 绑定套接字信息
        if (bind(listensock_, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            lg(Fatal, "bind error, error: %d, errstring: %s", errno, strerror(errno));
            exit(BindError);
        }
        lg(Info, "bind listensock_ success, listensock_: %d", listensock_);

        // tcp面向连接的，服务器一般是比较被动的，服务器一直处于一种，一直在等待连接到来的状态。
        //  监听
        if (listen(listensock_, backlog) < 0)
        {
            lg(Fatal, "listen error, error: %d, errstring: %s", errno, strerror(errno));
            exit(ListenError);
        }
        lg(Info, "listen listensock_ success, listensock_: %d", listensock_);

        
    }

    //静态成员函数不能直接访问类内非静态的成员
    // static void* Routine(void* args)
    // {
    //     pthread_detach(pthread_self());//线程分离
    //     ThreadData* td = static_cast<ThreadData*>(args);//强转
    //     td->tsv->server(td->sockfd, td->clientip, td->clientport);
    //     delete td;
    //     return nullptr;
    // }


    void Start()
    {   
        Daemon();    
        // 当我们对一个文件正在写入的时候，读端的文件描述符关闭，那么操作系统就会直接关闭该正在写入的进程
        signal(SIGPIPE, SIG_IGN);
        ThreadPool<Task>::GetInstance()->Start();  //启动线程池
        lg(Info, "tcpserver is running...");
        
        for (;;)
        {
            // 1.获取链接
            struct sockaddr_in client;
            socklen_t len = sizeof(client);
            int sockfd = accept(listensock_, (struct sockaddr *)&client, &len);
            if (sockfd < 0)
            {
                lg(Warning, "accept error, error: %d, errstring: %s", errno, strerror(errno));
                //如果获取链接失败我们就继续获取链接
                continue;
            }
            
            //获取客户端的信息
            uint16_t clientport = ntohs(client.sin_port);//网络转主机
            char clientip[32];
            //网络ip转字符串ip
            inet_ntop(AF_INET, &(client.sin_addr), clientip, sizeof(clientip));

            //2.根据新链接进行通信 
            lg(Info, "get a new link, sockfd: %d, clientip: %s, clientport: %d", sockfd, clientip, clientport);
            //version1 --单进程版
            //server(sockfd, clientip, clientport);
            //close(sockfd);


            // version2--多进程版
            // pid_t id = fork();
            // if(id == 0)
            // {
            //     // child
            //     close(listensock_);
            //     if(fork() > 0) exit(0);
            //     server(sockfd, clientip, clientport);//孙子进程，会被system领养
            //     close(sockfd);
            //     exit(0);
            // }
            
            // close(sockfd);
            // //father
            // pid_t rid = waitpid(id, nullptr, 0); 
            // (void)rid;

            //vesion 3---多线程版本
            // ThreadData *td = new ThreadData(sockfd, clientip, clientport, this);
            // pthread_t tid;
            // pthread_create(&tid, nullptr, Routine, td);
            // pthread_join()

            //version4 --线程池版
            Task t(sockfd, clientip, clientport);
            ThreadPool<Task>::GetInstance()->Push(t);
        }
    }

    // void server(int sockfd, std::string clientip, uint16_t clientport)
    // {
    //     char buffer[4096];
    //     while(true)
    //     {
    //         //把客户端发送给服务器的信息读取到buffer中
    //         ssize_t n = read(sockfd, buffer, sizeof(buffer));
    //         if(n > 0)
    //         {
    //             buffer[n] = 0;//当成字符串
    //             std::cout << "client say# " << buffer << std::endl;
    //             //在把读到的信息发送给客户端
    //             std::string string_echo = "tcpserver echo#";
    //             string_echo += buffer;
    //             write(sockfd, string_echo.c_str(), string_echo.size());
    //         }
    //         else if(n == 0)//客户端退出
    //         {
    //             lg(Info, "%s:%d quit, server close sockfd:%d", clientip.c_str(), clientport, sockfd);
    //             break;
    //         }
    //         else//读取失败
    //         {
    //             lg(Warning,  "read error, sockfd:%d, client ip: %s, client port: %d", sockfd, clientip.c_str(), clientport);
    //             break;
    //         }
    //     }   
    // }
    ~TcpServer()
    {
    }

private:
    int listensock_;     // 套接字文件描述符
    uint16_t port_;  // 端口号
    std::string ip_; // ip;
};
